`define MULT_BUFFER_SIZE 8
`define INST_TYPE_DIV 1'b1
`define CONVERT(x) (~(x) + 1'b1)
`include "../../include/cpu_defines.sv"
module multDivFunctionUnit(
    input logic clk,
	input logic resetn,
	input logic flush,
    input logic inst_valid,
    input logic [31: 0] inst_src1_unsign,
	input logic [31: 0] inst_src2_unsign,
	input logic [31: 0] inst_src1,
	input logic [31: 0] inst_src2,
	input logic [31: 0] inst_src3,
	input logic [31: 0] inst_src4,
    input logic inst_src1_issign,
    input logic inst_src2_issign,
    input logic [5: 0] inst_rd1,
    input logic [5: 0] inst_rd2,
    input logic [5: 0] inst_rob_index1,
	input logic [5: 0] inst_rob_index2,
	input logic [`EXC_CODE_BUS] inst_exccode,
    input logic [`MULTOP_BUS] inst_type,
    input logic inst_wb_receive,

	output logic [5: 0] next_rd,
	output logic [31: 0] out_src,
	output logic [5: 0] out_rd,
	output logic [5: 0] out_rob_index,
	output logic [`EXC_CODE_BUS] out_inst_exccode,
    
    output logic inst_end,
    output logic inst_acceptable
);

typedef struct packed {
    reg en;
    reg issign, sign_a, sign_b;
	logic [`MULTOP_BUS] _type;
    reg [5:0] rd1, rd2;
    reg [5:0] rob_index1, rob_index2;
	reg [`EXC_CODE_BUS] exccode;
}mult_buffer;

typedef struct packed {
    reg [31:0] res1, res2;
}mult_res_buffer;

typedef struct packed {
    reg en;
    reg [5:0] rd;
    reg [5:0] rob_index;
	reg [`EXC_CODE_BUS] exccode;
    reg [31:0] res;
}prefetch_t;

mult_buffer buffer[`MULT_BUFFER_SIZE - 1: 0];
mult_res_buffer src_buffer[`MULT_BUFFER_SIZE - 1: 0];
prefetch_t prefetch;

logic full;
logic [63:0] mult_result_unsigned, mult_result_signed;
logic [63:0] mult_result;

logic [31:0] remainder;
logic [31:0] quotient;
logic [63:0] div_result_raw;
logic [63: 0] hilo, mult_add_unsign, mult_add_sign;
logic mult_end;
logic div_end, div_end_raw;


logic [`MULT_BUFFER_SIZE - 1 : 0] ready;
logic [`MULT_BUFFER_SIZE - 1 : 0] free_index;
logic [`MULT_BUFFER_SIZE - 1 : 0] buffer_en;
logic [`MULT_BUFFER_SIZE - 1 : 0] src_buffer_en;
logic [`MULT_BUFFER_SIZE - 1 : 0] buffer_select_index;
logic [$clog2(`MULT_BUFFER_SIZE) - 1: 0] out_index_encode;
logic [$clog2(`MULT_BUFFER_SIZE) - 1: 0] buffer_select_index_encode;
logic [$clog2(`MULT_BUFFER_SIZE) - 1: 0] free_index_encode, select_index;
logic [$clog2(`MULT_BUFFER_SIZE)-1:0] div_index;
logic [$clog2(`MULT_BUFFER_SIZE)-1:0] mult_index;
logic [`MULTOP_BUS] type_next;
logic flush_next;
logic mult_type, div_type;

assign full = &buffer_en;
assign ready = buffer_en & src_buffer_en;
assign buffer_select_index = ready;
assign inst_acceptable  = ~full;
assign select_index = buffer_select_index_encode;
assign next_rd = prefetch.en ? prefetch.rd : buffer[select_index].rd1;
assign inst_end = |ready | prefetch.en;
assign div_end = div_end_raw & ~flush & ~flush_next;
assign mult_type = !div_type;
assign div_type = inst_type == `MULTOP_DIV || inst_type == `MULTOP_DIVU;

priority_selector8 selector_free_index(~buffer_en, free_index);
encoder_8to3 encoder_free_index(free_index, free_index_encode);
priority_encoder_8to3 encoder_select_index(buffer_select_index, buffer_select_index_encode);
integer i = 0;

always_ff @(posedge clk)begin
	flush_next <= flush;
	if(!prefetch.en)begin
		out_src <= buffer[select_index]._type == `MULTOP_MUL ? src_buffer[select_index].res1 : src_buffer[select_index].res2;
		out_rd <= buffer[select_index].rd1;
		out_rob_index <= buffer[select_index].rob_index1;
		out_inst_exccode <= buffer[select_index].exccode;
	end
	else begin
		out_src <= prefetch.res;
		out_rd <= prefetch.rd;
		out_rob_index <= prefetch.rob_index;
		out_inst_exccode <= prefetch.exccode;
	end
end

always_ff @(posedge clk) begin
    if(resetn == `RST_ENABLE || flush) begin
        for(i = 0; i < `MULT_BUFFER_SIZE; i ++) begin
            buffer_en[i] <= 0;
            buffer[i] <= 0;
            src_buffer[i] <= 0;
            src_buffer_en[i] <=0;
        end
        out_index_encode <= 0;
        mult_index <= 0;
        mult_end <= 0;
        prefetch <= `EXC_NONE;
		type_next <= 0;
		hilo <= 0;
    end
    else begin
//乘法只要一个周期
		type_next <= inst_type;
		hilo <= {inst_src4, inst_src3};
        if(inst_valid & mult_type) begin
            mult_index <= free_index_encode;
            mult_end <= 1;
        end
        else begin
            mult_index <= 0;
            mult_end <= 0;
        end
		if(div_end)begin
			src_buffer[div_index].res1 <= quotient;
			src_buffer[div_index].res2 <= remainder;
		end
		if(mult_end)begin
			src_buffer[mult_index].res1 <= mult_result[31: 0];
			src_buffer[mult_index].res2 <= mult_result[63: 32];
		end

		if(div_end)begin
			src_buffer_en[div_index] <= 1'b1;
		end
		if(mult_end)begin
			src_buffer_en[mult_index] <= 1'b1;
		end
		if(!prefetch.en && inst_wb_receive)begin
			src_buffer_en[select_index] <= 1'b0;
			buffer_en[select_index] <= 1'b0;
		end

        if(inst_valid) begin
            buffer[free_index_encode].issign <= inst_type == `MULTOP_DIV || inst_type == `MULTOP_MULT;
			buffer[free_index_encode]._type <= inst_type;
            buffer[free_index_encode].sign_a <= inst_src1_issign;
            buffer[free_index_encode].sign_b <= inst_src2_issign;
            buffer[free_index_encode].rob_index1 <= inst_rob_index1;
            buffer[free_index_encode].rob_index2 <= inst_rob_index2;
            buffer[free_index_encode].rd1 <= inst_rd1;
            buffer[free_index_encode].rd2 <= inst_rd2;
            buffer[free_index_encode].exccode <= inst_exccode;
            buffer_en[free_index_encode] <= 1;
        end
        
		if(!prefetch.en && inst_wb_receive)begin
			prefetch.en <= 1'b1;
			prefetch.rd <= buffer[select_index].rd2;
			prefetch.res <= buffer[select_index]._type == `MULTOP_MUL ? src_buffer[select_index].res2 : src_buffer[select_index].res1;
			prefetch.rob_index <= buffer[select_index].rob_index2;
			prefetch.exccode <= buffer[select_index].exccode;
		end
		if(prefetch.en && inst_wb_receive)begin
			prefetch.en <= 1'b0;
		end
        
    end
end

wire div_end_issign = buffer[div_index].issign;
wire div_end_sign_a = buffer[div_index].sign_a;
wire div_end_sign_b = buffer[div_index].sign_b;
always_comb begin
	case(type_next)
	`MULTOP_MADD, `MULTOP_MSUB: mult_result = mult_add_sign;
	`MULTOP_MADDU, `MULTOP_MSUBU: mult_result = mult_add_unsign;
	`MULTOP_MULTU: mult_result = mult_result_unsigned;
	default: mult_result = mult_result_signed;
	endcase
end
always_comb begin
    if(div_end && div_end_issign) begin
        case({div_end_sign_a, div_end_sign_b})
            2'b00 : begin 
                quotient  = div_result_raw[63:32];
                remainder = div_result_raw[31:0 ];
            end
            2'b01 :begin 
                quotient  = `CONVERT(div_result_raw[63:32]);
                remainder = div_result_raw[31:0];
            end
            2'b10 :begin
                quotient  = `CONVERT(div_result_raw[63:32]);
                remainder = `CONVERT(div_result_raw[31:0]);
            end
            2'b11 :begin
                quotient =  div_result_raw[63:32];
                remainder = `CONVERT(div_result_raw[31:0]);
            end
        endcase
    end
    else begin
        quotient =  div_result_raw[63:32];
        remainder = div_result_raw[31:0 ];
    end
end

mult_32 mult0(
	.CLK(clk),
	.SCLR(flush),
    .A(inst_src1), 
    .B(inst_src2), 
    .P(mult_result_unsigned)
);

Adder64_unsigned adder64_unsign(
	.A(mult_result_unsigned),
	.B(hilo),
	.ADD(type_next == `MULTOP_MADDU),
	.S(mult_add_unsign)
);

SignMult mult1(
	.CLK(clk),
	.SCLR(flush),
	.A(inst_src1),
	.B(inst_src2),
	.P(mult_result_signed)
);

Adder64_sign adder64_sign(
	.A(mult_result_signed),
	.B(hilo),
	.ADD(type_next == `MULTOP_MADD),
	.S(mult_add_sign)
);

Div_32 div_func(
	.aclk(clk),
	.aresetn(resetn && (!flush_next && !flush)),
	
	.s_axis_divisor_tvalid(inst_valid & div_type),
	.s_axis_divisor_tdata(inst_src2_unsign),
	.s_axis_dividend_tvalid(inst_valid & div_type),
	.s_axis_dividend_tuser(free_index_encode),
	.s_axis_dividend_tdata(inst_src1_unsign),

	.m_axis_dout_tvalid(div_end_raw),
	.m_axis_dout_tuser(div_index),
	.m_axis_dout_tdata(div_result_raw)
);

endmodule